<?php
/**
 *+------------------
 * Lflow
 *+------------------
 * Copyright (c) 2023~2030 gitee.com/liu_guan_qing All rights reserved.本版权不可删除，侵权必究
 *+------------------
 * Author: Mr.April(405784684@qq.com)
 *+------------------
 */

namespace app\adminapi\controller\v1\wf;

use app\adminapi\controller\AuthController;
use app\adminapi\controller\v1\wf\vo\WfHighlihtData;
use app\adminapi\controller\v1\wf\vo\WfHighlihtDataVO;
use app\Request;
use Exception;
use lflow\ckpt\DecisionCkpt;
use lflow\ckpt\EndCkpt;
use lflow\ckpt\NodeCkpt;
use lflow\ckpt\StartCkpt;
use lflow\core\QueryServices;
use lflow\core\services\OrderServices;
use lflow\core\WorkFlowEngine;
use lflow\exceptions\WorkFlowException;
use lflow\impl\JuelExpression;
use lflow\lib\enum\WfOrderStateEnum;
use lflow\lib\interface\WfConstants;
use lflow\lib\util\ObjectHelper;
use lflow\lib\util\StringHelper;
use lflow\model\HistoryOrderModel;
use lflow\model\HistoryTaskModel;
use think\App;
use think\facade\Db;

class Order extends AuthController implements WfConstants
{

    protected OrderServices $service;

    public function __construct(App $app, OrderServices $services)
    {
        parent::__construct($app);
        $this->services = $services;
    }

    /**
     * 资源列表
     *
     * @param \app\Request $request
     *
     * @return \think\response\Json
     */
    public function index(Request $request): \think\response\Json
    {
        $where = $this->request->getMore([
            ['creator', $request->adminId()],
        ]);
        $query = app()->make(QueryServices::class);
        return app('json')->success($query->getHistoryOrders($where));
    }

    /**
     * 获取指定资源
     *
     * @param string $id
     *
     * @return \think\Response\Json
     */
    public function read(string $id): \think\Response\Json
    {
        $ret = $this->service->get($id);
        if ($ret) {
            return app('json')->success($ret->toArray());
        } else {
            return app('json')->fail('参数错误');
        }
    }

    /**
     * 添加资源
     *
     * @param \app\Request $request
     *
     * @return \think\Response\Json
     */
    public function save(Request $request): \think\Response\Json
    {
        try {
            Db::startTrans();
            $data      = $this->request->postMore([['process_id'], ['args']]);
            $processId = $data['process_id'] ?? '';
            $args      = $data['args'] ?? [];
            if (empty($processId)) {
                return app('json')->fail('参数错误缺少流程ID');
            }
            //给表单追加当前用户信息
            $userInfo = $request->adminInfo();
            //构造参数
            $args = ObjectHelper::putAll((object)$args, (object)[
                WfConstants::ORDER_USER_REAL_NAME_KEY => $userInfo['real_name'] ?? '',
                WfConstants::ORDER_USER_NAME_KEY      => $userInfo['account'] ?? '',
                WfConstants::ORDER_USER_ID            => $userInfo['id'] ?? '',
            ]);

            $engine = app()->make(WorkFlowEngine::class);
            //创建流程
            $order = $engine->startInstanceById($processId, $request->adminId(), clone $args);
            //执行第一个审批节点
            $tasks = $engine->query()->getActiveTasks(['order_id' => $order->getData('id')]);
            if ($tasks['list'] != null && count($tasks['list']) > 0) {
                $task = $tasks['list'][0];
                $engine->executeTask($task->getData('id'), $userInfo['id'], clone $args);
            }
            Db::commit();
            return app('json')->success('操作成功');
        } catch (WorkFlowException $e) {
            Db::startTrans();
            return app('json')->fail($e->getMessage());
        }
    }

    /**
     * 更新指定资源
     *
     * @param \app\Request $request
     * @param string       $id
     *
     * @return \think\Response\Json
     */
    public function update(Request $request, string $id): \think\Response\Json
    {
        if (!$id || !($this->service->get($id))) {
            return app('json')->fail(100026);
        }
        $data = $this->request->post('content');
        if ($this->service->redeploy($id, json_decode($data, 1))) {
            return app('json')->success('操作成功');
        }
        return app('json')->fail('操作成功');
    }

    /**
     * 设置状态
     *
     * @param string     $id
     * @param string|int $status
     *
     * @return \think\Response\Json
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function setStatus(string $id, string|int $status): \think\Response\Json
    {
        $this->service->updateState($id, $status);
        return app('json')->success('操作成功');
    }

    /**
     * 流程图高亮显示数据
     *
     * @param \app\Request $request
     *
     * @return \think\Response\Json
     * @throws \PhpEnum\Exceptions\IllegalArgumentException
     * @throws \PhpEnum\Exceptions\InstantiationException
     */
//    public function highLightData(Request $request): \think\Response\Json
//    {
//        try {
//            $vo     = new WfHighlihtData();
//            $where  = $request->postMore([['id']]);
//            $engine = app()->make(WorkFlowEngine::class);
//            //工单历史记录
//            $historyOrder = $engine->query()->getHistOrder($where['id']);
//            if (empty($historyOrder)) {
//                throw new WorkFlowException("实例不存在或被删除");
//            }
//            //流程模型获取
//            $processModel = $engine->process()->getProcessById($historyOrder->getData('process_id'));
//
//            //历史任务获取
//            $historyTasks = $engine->query()->getHistoryWorkItems(['order_id' => $historyOrder->getData('id')]);
//
//            //进行中的任务
//            $tasks    = $engine->query()->getActiveTasks(['order_id' => $historyOrder->getData('id')])['list'];
//            $nodeCkpt = $processModel->getData('process_ckpt')->getstart();
//            foreach ($tasks as $task) {
//                if (!in_array($task->getData('task_name'), $vo->getActiveNodeNames())) {
//                    $activeNodeNames   = $vo->getActiveNodeNames();
//                    $activeNodeNames[] = $task->getData('task_name');
//                    $vo->setActiveNodeNames($activeNodeNames);
//                    $this->recursionModel($nodeCkpt, $historyOrder, $historyTasks['list'], $task->getData('task_name'), $vo);
//                }
//            }
//
//            $endCkpts = $processModel->getData('process_ckpt')->getCkpts(new EndCkpt());
//            if ($endCkpts != null && empty($data['activeNodeNames'])) {
//                foreach ($endCkpts as $endCkpt) {
//                    $this->recursionModel($nodeCkpt, $historyOrder, $historyTasks['list'], $endCkpt->getName(), $vo);
//                }
//            };
//            return app('json')->success($vo->toArray());
//        } catch (WorkFlowException $e) {
//            return app('json')->fail($e->getMessage());
//        }
//    }

    public function highLightData(Request $request): \think\Response\Json
    {
        try {
            $vo     = new WfHighlihtData();
            $where  = $request->postMore([['id']]);
            $engine = app()->make(WorkFlowEngine::class);
            //工单历史记录
            $historyOrder = $engine->query()->getHistOrder($where['id']);
            if (empty($historyOrder)) {
                throw new WorkFlowException("实例不存在或被删除");
            }
            //流程模型获取
            $processModel = $engine->process()->getProcessById($historyOrder->getData('process_id'));

            //历史任务获取
            $historyTasks = $engine->query()->getHistoryWorkItems(['order_id' => $historyOrder->getData('id')]);

            //进行中的任务
            $tasks    = $engine->query()->getActiveTasks(['order_id' => $historyOrder->getData('id')])['list'];
            $nodeCkpt = $processModel->getData('process_ckpt')->getstart();
            foreach ($tasks as $task) {
                if (!in_array($task->getData('task_name'), $vo->getActiveNodeNames())) {
                    $activeNodeNames   = $vo->getActiveNodeNames();
                    $activeNodeNames[] = $task->getData('task_name');
                    $vo->setActiveNodeNames($activeNodeNames);
                    $this->recursionModel($nodeCkpt, $historyOrder, $historyTasks['list'], $task->getData('task_name'), $vo);
                }
            }

            $endCkpts = $processModel->getData('process_ckpt')->getCkpts(new EndCkpt());
            if ($endCkpts != null && empty($vo->getActiveNodeNames())) {
                foreach ($endCkpts as $endCkpt) {
                    $this->recursionModel($nodeCkpt, $historyOrder, $historyTasks['list'], $endCkpt->getName(), $vo);
                }
            };
            return app('json')->success($vo->toArray());
        } catch (WorkFlowException $e) {
            return app('json')->fail($e->getMessage());
        }
    }

    /**
     * 取回流程
     *
     * @param \app\Request $request
     *
     * @return \think\Response\Json
     */
    public function takeBack(Request $request): \think\Response\Json
    {
        try {
            Db::startTrans();
            $userInfo = $request->adminInfo();
            $where    = $request->postMore([['id', '', '', 'order_id']]);
            $engine   = app()->make(WorkFlowEngine::class);
            $order    = $engine->Query()->getOrder($where['order_id']);
            if (empty($order)) {
                throw new WorkFlowException('工单实例不存在或被删除');
            }

            $tasks = $engine->Query()->getActiveTasks($where);
            if (empty($tasks['list']->toArray())) {
                throw new WorkFlowException('缺失任务信息');
            }

            // 1.取回流程
            // 1.1 给流程实例追加额外参数
            $task              = $tasks['list'][0];
            $args              = new \stdClass();
            $args->order_state = WfOrderStateEnum::TAKE_BACK()->ordinal();
            $args->remark      = "【" . $userInfo['real_name'] . "】 取回流程";
            $engine->order()->addVariable($order->getData('id'), $args);

            // 1.2 直接跳到结束节点
            $processModel = $engine->process()->getProcessById($order->getData('process_id'));
            $engine->executeAndJumpTask($task->getData('id'), $engine::ADMIN, $args, $processModel->getData('process_ckpt')->getCkpts(new EndCkpt())[0]->getName());
            Db::commit();
            return app('json')->success('操作成功');
        } catch (WorkFlowException|\Exception  $e) {
            Db::rollback();
            return app('json')->fail($e->getMessage());
        }
    }

    /**
     * 结束流程
     *
     * @param \app\Request $request
     *
     * @return \think\Response\Json
     */
    public function undo(Request $request): \think\Response\Json
    {
        try {
            Db::startTrans();
            $userInfo = $request->adminInfo();
            $where    = $request->postMore([['id', '', '', 'order_id']]);
            $engine   = app()->make(WorkFlowEngine::class);
            $order    = $engine->Query()->getOrder($where['order_id']);
            if (empty($order)) {
                throw new WorkFlowException('工单实例不存在或被删除');
            }
            $tasks = $engine->Query()->getActiveTasks($where);
            if (empty($tasks['list']->toArray())) {
                throw new WorkFlowException('缺失任务信息');
            }

            // 1.取回流程
            // 1.1 给流程实例追加额外参数
            $task              = $tasks['list'][0];
            $args              = new \stdClass();
            $args->order_state = WfOrderStateEnum::CANCEL()->ordinal();
            $args->remark      = "【" . $userInfo['real_name'] . "】 作废流程";
            $engine->order()->addVariable($order->getData('id'), $args);

            // 1.2 直接跳到结束节点
            $processModel = $engine->process()->getProcessById($order->getData('process_id'));
            $engine->executeAndJumpTask($task->getData('id'), $engine::ADMIN, $args, $processModel->getData('process_ckpt')->getCkpts(new EndCkpt())[0]->getName());
            Db::commit();
            return app('json')->success('操作成功');
        } catch (WorkFlowException|\Exception $e) {
            Db::rollback();
            return app('json')->fail($e->getMessage());
        }
    }

    /**
     * 删除流程实例所有数据
     *
     * @param \app\Request $request
     *
     * @return \think\Response\Json
     */
    public function cascadeRemove(Request $request): \think\Response\Json
    {
        try {
            Db::startTrans();
            $param  = $request->postMore([['id']]);
            $engine = app()->make(WorkFlowEngine::class);
            $order  = $engine->query()->getOrder($param['id']);
            if (!empty($order)) {
                throw new Exception("进行中的流程实例不能删除");
            }
            $engine->order()->cascadeRemove($param['id']);
            Db::commit();
            return app('json')->success('操作成功');
        } catch (WorkFlowException|\Exception $e) {
            Db::rollback();
            return app('json')->fail($e->getMessage());
        }
    }

    /**
     * @param \app\Request $request
     *
     * @return \think\Response\Json
     */
    public function get(Request $request): \think\Response\Json
    {
        $engine = app()->make(WorkFlowEngine::class);
        $where  = $request->postMore([['id']]);
        $order  = $engine->query()->getHistOrders($where['id']);
        if (!empty($order)) {
            $processModle = $order->getData('processs');
            if (!empty($processModle)) {
                $process = $processModle->getData('content');
                if (is_string($process) && !empty($process)) {
                    //如果是字符串&不等于空进行json处理
                    $processModle->set('content', json_decode($process));
                }
            }
        } else {
            return app('json')->fail('实例不存在或被删除');
        }
        return app('json')->success($order->toArray());
    }


    /**
     * 高亮上下文逻辑处理
     * @param $nodeCkpt
     * @param $historyOrder
     * @param $historyTasks
     * @param $taskName
     * @param $vo
     *
     * @throws \PhpEnum\Exceptions\IllegalArgumentException
     * @throws \PhpEnum\Exceptions\InstantiationException
     */
    private function recursionModel($nodeCkpt, $historyOrder, $historyTasks, $taskName, $vo)
    {
        if ($nodeCkpt->getName() === $taskName) {
            if ($nodeCkpt instanceof EndCkpt) {
                $historyNodeNames   = $vo->getHistoryNodeNames();
                $historyNodeNames[] = $nodeCkpt->getName();
                $vo->setHistoryNodeNames($historyNodeNames);

            }
            return;
        }

        // 拿到非正常结束的流程实例状态值
        $orderStatusList = array_filter(WfOrderStateEnum::values(), function ($key) {
            return $key != WfOrderStateEnum::DOING()->name() && $key != WfOrderStateEnum::FINISHED()->name();
        });
        $orderStatusList = array_map(function ($item) {
            return $item->getId();
        }, $orderStatusList);
        $orderStatusList = array_values($orderStatusList);

        // 非正常结束，需要进行特殊处理

        if (in_array(ObjectHelper::getObjectValue($historyOrder->getData('variable'), WfConstants::ORDER_STATE_KEY), $orderStatusList)
            && $nodeCkpt->getName() === $historyTasks[0]->getTaskName()) {
            $vo->getHistoryNodeNames()->add($nodeCkpt->getName());
            return;
        }

        //对象上下文处理
        if (!in_array($nodeCkpt->getName(), $vo->getHistoryNodeNames())) {
            $historyNodeNames   = $vo->getHistoryNodeNames();
            $historyNodeNames[] = $nodeCkpt->getName();
            $vo->setHistoryNodeNames($historyNodeNames);

            foreach ($nodeCkpt->getOutputs() as $value) {
                if (isset($nodeCkpt->outputs)) {
                    $defaultDecisionInputModel = null;
                    $historyTask               = null;
                    //默认取决策节点前面第一个节点为任务节点
                    if ($nodeCkpt instanceof DecisionCkpt) {
                        $defaultDecisionInputModel      = $nodeCkpt->getInputs()[0]->getSource();
                        $finalDefaultDecisionInputModel = $defaultDecisionInputModel;
                        foreach ($historyTasks as $hisTask) {
                            if (StringHelper::equalsIgnoreCase($hisTask->getData('task_name'), $finalDefaultDecisionInputModel->getName())) {
                                $historyTask = $hisTask;
                            }
                        }
                    }
                    //表达式处理
                    $args = $historyOrder->getData('variable');
                    if ($historyTask != null) {
                        $taskArgs = $historyTask->getData('variable');
                        $args     = ObjectHelper::putAll($args, $taskArgs);
                    }
                    if (StringHelper::isNotEmpty($value->getExpr()) && $nodeCkpt instanceof DecisionCkpt && $defaultDecisionInputModel != null) {
                        $Expression = new JuelExpression();
                        foreach ($nodeCkpt->getOutputs() as $tm) {
                            $expr = $tm->getExpr();
                            if (StringHelper::isNotEmpty($expr) && $Expression->eval($expr, $args)) {
                                if (StringHelper::equalsIgnoreCase($value->getTo(), $tm->getTo())) {
                                    $history_edge_names   = $vo->getHistoryEdgeNames();
                                    $history_edge_names[] = $tm->getName();
                                    $vo->setHistoryEdgeNames($history_edge_names);
                                    $this->recursionModel($tm->getTarget(), $historyOrder, $historyTasks, $taskName, $vo);
                                    return;
                                }
                            }
                        }
                    }
                }

                if (!in_array($value->getName(), $vo->getHistoryEdgeNames())) {
                    //排除表达式ckpt
                    if (!$value->getSource() instanceof DecisionCkpt) {
                        $history_edge_names   = $vo->getHistoryEdgeNames();
                        $history_edge_names[] = $value->getName();
                        $vo->setHistoryEdgeNames($history_edge_names);
                        $this->recursionModel($value->getTarget(), $historyOrder, $historyTasks, $taskName, $vo);
                    }
                }
            }
        }
    }
}
